From b171a0d5e05d0779dad1a905420b14b6cc18b1c1 Mon Sep 17 00:00:00 2001 From: Tim Deegan Date: Wed, 2 May 2007 14:41:51 +0100 Subject: [PATCH] [HVM] Save/restore: save pending interrupts/events on HVM vcpus along with the rest of the cpu state. This stops us from dropping interrupts after the vlapic has recorded them in the ISR but before the guest has actually taken them. Signed-off-by: Tim Deegan --- xen/arch/x86/hvm/svm/svm.c | 61 ++++++++++++++++++++++++++++++-- xen/arch/x86/hvm/vmx/vmx.c | 66 +++++++++++++++++++++++++++++++++++ xen/include/public/hvm/save.h | 14 ++++++++ 3 files changed, 139 insertions(+), 2 deletions(-) diff --git a/xen/arch/x86/hvm/svm/svm.c b/xen/arch/x86/hvm/svm/svm.c index d0afe8d656..f74bdd9c3b 100644 --- a/xen/arch/x86/hvm/svm/svm.c +++ b/xen/arch/x86/hvm/svm/svm.c @@ -307,6 +307,41 @@ int svm_vmcb_save(struct vcpu *v, struct hvm_hw_cpu *c) c->sysenter_esp = vmcb->sysenter_esp; c->sysenter_eip = vmcb->sysenter_eip; + /* Save any event/interrupt that was being injected when we last + * exited. Although there are three(!) VMCB fields that can contain + * active events, we only need to save at most one: because the + * intr_assist logic never delivers an IRQ when any other event is + * active, we know that the only possible collision is if we inject + * a fault while exitintinfo contains a valid event (the delivery of + * which caused the last exit). In that case replaying just the + * first event should cause the same behaviour when we restore. */ + if ( vmcb->vintr.fields.irq + && /* Check it's not a fake interrupt (see svm_intr_assist()) */ + !(vmcb->general1_intercepts & GENERAL1_INTERCEPT_VINTR) ) + { + c->pending_vector = vmcb->vintr.fields.vector; + c->pending_type = 0; /* External interrupt */ + c->pending_error_valid = 0; + c->pending_reserved = 0; + c->pending_valid = 1; + c->error_code = 0; + } + else if ( vmcb->exitintinfo.fields.v ) + { + c->pending_event = vmcb->exitintinfo.bytes & 0xffffffff; + c->error_code = vmcb->exitintinfo.fields.errorcode; + } + else if ( vmcb->eventinj.fields.v ) + { + c->pending_event = vmcb->eventinj.bytes & 0xffffffff; + c->error_code = vmcb->eventinj.fields.errorcode; + } + else + { + c->pending_event = 0; + c->error_code = 0; + } + return 1; } @@ -335,7 +370,7 @@ int svm_vmcb_restore(struct vcpu *v, struct hvm_hw_cpu *c) if ( !svm_paging_enabled(v) ) { - printk("%s: paging not enabled.", __func__); + printk("%s: paging not enabled.\n", __func__); goto skip_cr3; } @@ -436,11 +471,33 @@ int svm_vmcb_restore(struct vcpu *v, struct hvm_hw_cpu *c) vmcb->dr6 = c->dr6; vmcb->dr7 = c->dr7; + if ( c->pending_valid ) + { + gdprintk(XENLOG_INFO, "Re-injecting 0x%"PRIx32", 0x%"PRIx32"\n", + c->pending_event, c->error_code); + + /* VMX uses a different type for #OF and #BP; fold into "Exception" */ + if ( c->pending_type == 6 ) + c->pending_type = 3; + /* Sanity check */ + if ( c->pending_type == 1 || c->pending_type > 4 + || c->pending_reserved != 0 ) + { + gdprintk(XENLOG_ERR, "Invalid pending event 0x%"PRIx32"\n", + c->pending_event); + return -EINVAL; + } + /* Put this pending event in exitintinfo and svm_intr_assist() + * will reinject it when we return to the guest. */ + vmcb->exitintinfo.bytes = c->pending_event; + vmcb->exitintinfo.fields.errorcode = c->error_code; + } + paging_update_paging_modes(v); return 0; bad_cr3: - gdprintk(XENLOG_ERR, "Invalid CR3 value=0x%"PRIx64"", c->cr3); + gdprintk(XENLOG_ERR, "Invalid CR3 value=0x%"PRIx64"\n", c->cr3); return -EINVAL; } diff --git a/xen/arch/x86/hvm/vmx/vmx.c b/xen/arch/x86/hvm/vmx/vmx.c index 1c47aadebc..5cc2674c85 100644 --- a/xen/arch/x86/hvm/vmx/vmx.c +++ b/xen/arch/x86/hvm/vmx/vmx.c @@ -370,6 +370,8 @@ static inline void __restore_debug_registers(struct vcpu *v) int vmx_vmcs_save(struct vcpu *v, struct hvm_hw_cpu *c) { + uint32_t ev; + c->rip = __vmread(GUEST_RIP); c->rsp = __vmread(GUEST_RSP); c->rflags = __vmread(GUEST_RFLAGS); @@ -436,6 +438,28 @@ int vmx_vmcs_save(struct vcpu *v, struct hvm_hw_cpu *c) c->sysenter_esp = __vmread(GUEST_SYSENTER_ESP); c->sysenter_eip = __vmread(GUEST_SYSENTER_EIP); + /* Save any event/interrupt that was being injected when we last + * exited. IDT_VECTORING_INFO_FIELD has priority, as anything in + * VM_ENTRY_INTR_INFO_FIELD is either a fault caused by the first + * event, which will happen the next time, or an interrupt, which we + * never inject when IDT_VECTORING_INFO_FIELD is valid.*/ + if ( (ev = __vmread(IDT_VECTORING_INFO_FIELD)) & INTR_INFO_VALID_MASK ) + { + c->pending_event = ev; + c->error_code = __vmread(IDT_VECTORING_ERROR_CODE); + } + else if ( (ev = __vmread(VM_ENTRY_INTR_INFO_FIELD)) + & INTR_INFO_VALID_MASK ) + { + c->pending_event = ev; + c->error_code = __vmread(VM_ENTRY_EXCEPTION_ERROR_CODE); + } + else + { + c->pending_event = 0; + c->error_code = 0; + } + return 1; } @@ -563,6 +587,48 @@ int vmx_vmcs_restore(struct vcpu *v, struct hvm_hw_cpu *c) vmx_vmcs_exit(v); paging_update_paging_modes(v); + + if ( c->pending_valid ) + { + vmx_vmcs_enter(v); + gdprintk(XENLOG_INFO, "Re-injecting 0x%"PRIx32", 0x%"PRIx32"\n", + c->pending_event, c->error_code); + + /* SVM uses type 3 ("Exception") for #OF and #BP; VMX uses type 6 */ + if ( c->pending_type == 3 + && (c->pending_vector == 3 || c->pending_vector == 4) ) + c->pending_type = 6; + + /* For software exceptions, we need to tell the hardware the + * instruction length as well (hmmm). */ + if ( c->pending_type > 4 ) + { + int addrbytes, ilen; + if ( (c->cs_arbytes & (1u<<13)) && (c->msr_efer & EFER_LMA) ) + addrbytes = 8; + else if ( (c->cs_arbytes & (1u<<14)) ) + addrbytes = 4; + else + addrbytes = 2; + ilen = hvm_instruction_length(c->rip, hvm_guest_x86_mode(v)); + __vmwrite(VM_ENTRY_INSTRUCTION_LEN, ilen); + } + + /* Sanity check */ + if ( c->pending_type == 1 || c->pending_type > 6 + || c->pending_reserved != 0 ) + { + gdprintk(XENLOG_ERR, "Invalid pending event 0x%"PRIx32"\n", + c->pending_event); + return -EINVAL; + } + /* Re-inject the exception */ + __vmwrite(VM_ENTRY_INTR_INFO_FIELD, c->pending_event); + __vmwrite(VM_ENTRY_EXCEPTION_ERROR_CODE, c->error_code); + v->arch.hvm_vmx.vector_injected = 1; + vmx_vmcs_exit(v); + } + return 0; bad_cr3: diff --git a/xen/include/public/hvm/save.h b/xen/include/public/hvm/save.h index effa3e39ba..e01218a9d6 100644 --- a/xen/include/public/hvm/save.h +++ b/xen/include/public/hvm/save.h @@ -182,6 +182,20 @@ struct hvm_hw_cpu { /* guest's idea of what rdtsc() would return */ uint64_t tsc; + + /* pending event, if any */ + union { + uint32_t pending_event; + struct { + uint8_t pending_vector:8; + uint8_t pending_type:3; + uint8_t pending_error_valid:1; + uint32_t pending_reserved:19; + uint8_t pending_valid:1; + }; + }; + /* error code for pending event */ + uint32_t error_code; }; DECLARE_HVM_SAVE_TYPE(CPU, 2, struct hvm_hw_cpu); -- 2.30.2